
#include "FileOperation.h"
using namespace Pan;

#include <windows.h>
#include <stdio.h>

#ifndef F_OK
#define F_OK (0)
#endif

bool FileOperation::is_existing_path(const wchar_t *path)
{
    // 保存文件属性
    DWORD fa(INVALID_FILE_ATTRIBUTES);

    // 防止其他函数调用错误的干扰
    ::SetLastError(ERROR_SUCCESS);

    // 获取文件属性
    fa = ::GetFileAttributesW(path);

    // 判断结果
    if (fa == INVALID_FILE_ATTRIBUTES)
    {
        // 获取文件属性失败
        // 只要错误代码不是：找不到指定路径
        // 检查错误代码
        DWORD error = ::GetLastError();
        return error != ERROR_PATH_NOT_FOUND &&
               error != ERROR_FILE_NOT_FOUND;
    }
    else
    {
        // 获取文件属性成功，路径存在
        return true;
    }
}

bool FileOperation::is_existing_directory(const wchar_t *path)
{
    DWORD fa;

    // 防止其他函数调用错误的干扰
    ::SetLastError(ERROR_SUCCESS);

    // 获取文件属性
    fa = ::GetFileAttributesW(path);

    if (fa == INVALID_FILE_ATTRIBUTES)
    {
        // 无法获取文件属性，可能是因为不存在
        // 检查错误代码
        DWORD error = ::GetLastError();

        return error != ERROR_PATH_NOT_FOUND &&
               error != ERROR_FILE_NOT_FOUND;
    }
    else
    {
        // 属性中保存文件夹属性
        return (fa & FILE_ATTRIBUTE_DIRECTORY) != 0;
    }
}

bool FileOperation::is_empty_directory(const wchar_t *path)
{
    // 空指针和空字符串检查
    if (path == nullptr || path[0] == '\0')
        return false;

    // 分配缓冲区保存目录的绝对路径
    const unsigned buffer_size(512); // 缓存区的大小
    wchar_t *fullpath = new wchar_t[buffer_size];

    if (fullpath == nullptr)
        return false; // 内存分配失败，无法判断

    bool is_empty = false; // 返回值
    WIN32_FIND_DATAW data; // FindFirstFileW和FindNextFileW要用的参数
    HANDLE hfind;          // 查找句柄

    // 转成绝对路径
    DWORD len = ::GetFullPathNameW(path, buffer_size, fullpath, NULL);

    if (len <= 0 || len >= buffer_size)
    { // 转换失败
        is_empty = false;
        goto end;
    }

    // 拼接"\\*"
    if (len > 0 && fullpath[len - 1] != '\\')
    {
        fullpath[len++] = '\\';
    }
    fullpath[len++] = '*';
    fullpath[len] = '\0';

    // 开始查找
    hfind = ::FindFirstFileW(fullpath, &data);

    if (hfind != NULL && hfind != INVALID_HANDLE_VALUE)
    {
        is_empty = true; // 假设文件夹是空的
        do
        {
            const wchar_t *n = data.cFileName;
            if ((n[0] == '.' && n[1] == '\0') ||
                (n[0] == '.' && n[1] == '.' && n[2] == '\0'))
            { // 除了"."和".."之外没有其它东西，这个目录就是空
                continue;
            }

            // 找到了其它的东西，说明目录非空
            is_empty = false;
            break;
        } while (::FindNextFileW(hfind, &data));

        // 关闭查找
        ::FindClose(hfind);
        hfind = NULL;
    }
    else
    {
        // 查找文件夹失败，无法确定
        is_empty = false;
    }

end:
    // 释放内存并返回
    delete[] fullpath;
    fullpath = nullptr;
    return is_empty;
}

bool FileOperation::is_existing_file(const wchar_t *path)
{
    HANDLE hfile(NULL); // 文件句柄

    // 重新设置错误代码，避免发生意外
    ::SetLastError(ERROR_SUCCESS);

    // 直接打开文件
    hfile = ::CreateFileW //
        (
            path,
            FILE_READ_EA,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING, // 打开一个存在的文件
            0,
            NULL //
        );

    DWORD error = ::GetLastError();

#ifdef DEBUG
    printf("hfile: %p\n", hfile);
    printf("lastError: %lu\n", error);
#endif

    if (hfile == NULL || hfile == INVALID_HANDLE_VALUE)
    {
        // 打开文件失败，检查错误代码
        // 注意：有时候即使文件存在，也可能会打开失败，如拒绝访问的情况
        return error != ERROR_PATH_NOT_FOUND &&
               error != ERROR_FILE_NOT_FOUND;
    }
    else
    {
        // 打开成功，文件存在

        // 记得关闭句柄释放资源
        ::CloseHandle(hfile);
        hfile = NULL;

        return true;
    }
}

bool FileOperation::copy_file(const wchar_t *src_path, const wchar_t *dest_path)
{
    return ::CopyFileW //
        (
            src_path,
            dest_path,
            FALSE // 表示如果目标文件存在, 那就覆盖它
        );
}

bool FileOperation::delete_file(const wchar_t *filepath)
{

    if (::DeleteFileW(filepath))
        return true; // 第一次直接删除成功

    // 如果失败, 去掉只读属性, 尝试再删除

    // 获取文件属性
    DWORD a = ::GetFileAttributesW(filepath);

    // 获取属性失败
    if (a == INVALID_FILE_ATTRIBUTES)
    {
        return false;
    }
    else
    {
        // 去除只读属性
        a &= ~FILE_ATTRIBUTE_READONLY;

        // 重新设置属性
        if (::SetFileAttributesW(filepath, a))
        {
            // 再次尝试删除
            return ::DeleteFileW(filepath);
        }
        else
        { // 取消只读属性失败
            return false;
        }
    }
}

unsigned long long FileOperation::
    get_file_size(const wchar_t *filepath, bool *pok)
{

    HANDLE hfile(INVALID_HANDLE_VALUE); // 文件句柄

    // 打开文件
    hfile = ::CreateFileW //
        (
            filepath,
            FILE_READ_EA,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL //
        );

    // 打开文件失败
    if (hfile == NULL || hfile == INVALID_HANDLE_VALUE)
    {
        if (pok != nullptr)
        {
            *pok = false;
        }
        return 0;
    }

    unsigned long long size(0);
    LARGE_INTEGER lsize;

    if (::GetFileSizeEx(hfile, &lsize))
    {
        // 获取大小成功
        if (lsize.QuadPart >= 0)
            size = lsize.QuadPart;
    }

    // 关闭句柄
    ::CloseHandle(hfile);

    return size;
}

bool FileOperation::create_directory(const wchar_t *path)
{
    return ::CreateDirectoryW //
        (
            path,
            NULL // 表示使用默认的安全属性
        );
}

bool FileOperation::create_directories(const wchar_t *path)
{
    // 参数示例: "C:\Program Files\Java\jdk1.8.0_251_x64_portable"

    // 空指针检测
    if (path == nullptr)
        return false;

    bool exists = FileOperation::is_existing_directory(path);

    // 已经存在
    if (exists)
    {
        return true;
    }

    wchar_t *fullpath(nullptr);

    // 分配内存
    fullpath = new wchar_t[512];

    // 内存分配失败
    if (fullpath == nullptr)
    {
        return false;
    }

    // 转成绝对路径
    DWORD len = ::GetFullPathNameW //
        (
            path,
            512,
            fullpath,
            NULL //
        );

    // 转换失败
    if (len <= 0 || len >= 512)
    {
        goto exit;
    }

    // 创建所需要的父路径
    for (DWORD i = 0; i < len; i++)
    {
        if (fullpath[i] == '\\')
        {
            fullpath[i] = '\0';

            // 直接创建，不用理会父路径已经存在的情况
            FileOperation::create_directory(fullpath);

            fullpath[i] = '\\';
        }
    }

    // 创建最后的路径
    FileOperation::create_directory(fullpath);

    exists = FileOperation::is_existing_directory(fullpath);

exit:
    delete[] fullpath, fullpath = nullptr;
    return exists;
}

bool FileOperation::get_file_last_write_time //
    (const wchar_t *filepath, DWORD64 *ptime)
{

    // 简单的参数检查
    if (filepath == nullptr)
        return false;

    if (ptime == nullptr)
        return false;

    // 文件句柄
    HANDLE hfile(NULL);

    // 打开文件
    hfile = ::CreateFileW //
        (
            filepath,
            FILE_READ_EA,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL //
        );

    // 打开文件失败
    if (hfile == INVALID_HANDLE_VALUE || hfile == nullptr)
    {
        return false;
    }

    FILETIME ftime;
    bool success(true);

    ftime.dwLowDateTime = 0;
    ftime.dwHighDateTime = 0;

    // 获取修改时间
    success = ::GetFileTime(hfile, NULL, NULL, &ftime);

    if (success)
    {
        *ptime = 0;
        *ptime |= ftime.dwHighDateTime;
        *ptime <<= (sizeof(ftime.dwLowDateTime) * 8);
        *ptime |= ftime.dwLowDateTime;
    }

    // 关闭文件句柄
    ::CloseHandle(hfile);

    return success;
}
